﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework;

namespace ShevaEngine.Core.Modules.Graphics.Foundation
{
    /// <summary>
    /// Sphere object.
    /// </summary>
    public class Sphere
    {
        /// <summary>Singleton.</summary>
        public static readonly Sphere Instance = new Sphere(50, 50, 1);

        /// <summary>Vertex buffer.</summary>
        private VertexBuffer VertexBuffer;
        /// <summary>Vertex buffer.</summary>
        private IndexBuffer IndexBuffer;
        /// <summary>Number vertices.</summary>
        private int NumberVertices;
        /// <summary>Number vertices.</summary>
        private int NumberIndices;

        /// <summary>
        /// Creates a sphere model.
        /// </summary>
        /// <param name="stacks">Stacks</param>
        /// <param name="slices">Slices</param>
        /// <param name="radius">Radius</param>
        public Sphere(int stacks, int slices, float radius)
        {
            // Calculates the resulting number of vertices and indices
            this.NumberVertices = (stacks + 1) * (slices + 1);
            this.NumberIndices = (3 * stacks * (slices + 1)) * 2;
            
            int[] indices = new int[this.NumberIndices];
            VertexPositionNormalTexture[] vertices = new VertexPositionNormalTexture[this.NumberVertices];

            float stackAngle = MathHelper.Pi / (float)stacks;
            float sliceAngle = (float)(Math.PI * 2.0) / (float)slices;

            // Generate the group of Stacks for the sphere  
            int wVertexIndex = 0;
            int vertexCount = 0;
            int indexCount = 0;

            for (int stack = 0; stack < (stacks + 1); stack++)
            {

                float r = (float)Math.Sin((float)stack * stackAngle);
                float y = (float)Math.Cos((float)stack * stackAngle);

                // Generate the group of segments for the current Stack  
                for (int slice = 0; slice < (slices + 1); slice++)
                {
                    float x = r * (float)Math.Sin((float)slice * sliceAngle);
                    float z = r * (float)Math.Cos((float)slice * sliceAngle);
                    vertices[vertexCount].Position = new Vector3(x * radius, y * radius, z * radius);

                    vertices[vertexCount].Normal = Vector3.Normalize(new Vector3(x, y, z));

                    vertices[vertexCount].TextureCoordinate = new Vector2((float)slice / (float)slices, (float)stack / (float)stacks);
                    vertexCount++;
                    if (stack != (stacks - 1))
                    {
                        // First Face
                        indices[indexCount++] = wVertexIndex + (slices + 1);

                        indices[indexCount++] = wVertexIndex;

                        indices[indexCount++] = wVertexIndex + 1;

                        // Second Face
                        indices[indexCount++] = wVertexIndex + (slices);

                        indices[indexCount++] = wVertexIndex;

                        indices[indexCount++] = wVertexIndex + (slices + 1);

                        wVertexIndex++;
                    }
                }
            }
            
            this.VertexBuffer = new VertexBuffer(ShevaEngine.Instance.GraphicsDevice, 
                typeof(VertexPositionNormalTexture), this.NumberVertices, BufferUsage.None);
            this.VertexBuffer.SetData(vertices, 0, vertices.Length);

            this.IndexBuffer = new IndexBuffer(ShevaEngine.Instance.GraphicsDevice,
                typeof(int), this.NumberIndices, BufferUsage.None);
            this.IndexBuffer.SetData(indices, 0, indices.Length);
        }

        /// <summary>
        /// Render a screen plane.
        /// </summary>
        public void Render()
        {
            ShevaEngine.Instance.GraphicsDevice.SetVertexBuffer(this.VertexBuffer);
            ShevaEngine.Instance.GraphicsDevice.Indices = this.IndexBuffer;

            ShevaEngine.Instance.GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, 
                this.NumberVertices, 0, this.NumberIndices / 3);
        }
    }
}
